package com.agentframework.autoconfigure;

import com.agentframework.core.agent.AutoThinkReactStrategy;
import com.agentframework.core.agent.DefaultReactStrategy;
import com.agentframework.core.agent.InOrderReactStrategy;
import com.agentframework.core.agent.ReactStrategy;
import com.agentframework.core.memory.AgentInMemoryChatMemory;
import com.agentframework.core.plan.Planner;
import com.agentframework.core.plan.WritePlanAction;
import com.agentframework.core.provider.*;
import com.agentframework.core.tools.AgentToolCallingManager;
import com.agentframework.core.tools.ToolManager;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.mcp.client.autoconfigure.McpClientAutoConfiguration;
import org.springframework.ai.mcp.client.autoconfigure.McpToolCallbackAutoConfiguration;
import org.springframework.ai.mcp.client.autoconfigure.StdioTransportAutoConfiguration;
import org.springframework.ai.mcp.server.autoconfigure.McpServerAutoConfiguration;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.tool.execution.ToolExecutionExceptionProcessor;
import org.springframework.ai.tool.resolution.ToolCallbackResolver;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Primary;

/**
 * @author wangjun
 **/
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration({ McpClientAutoConfiguration.class, StdioTransportAutoConfiguration.class, McpServerAutoConfiguration.class, McpToolCallbackAutoConfiguration.class })
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableConfigurationProperties(LlmProperties.class)
public class AgentAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Provider provider() {
        return new DefaultProvider();
    }

    @Primary
    @Bean
    @ConditionalOnMissingBean
    public Llm llm(LlmProperties llmProperties, Provider provider, ChatMemory chatMemory) {
        return new Llm(provider, chatMemory, llmProperties);
    }

    @Bean
    @ConditionalOnMissingBean
    public HumanLlm humanLlm() {
        return new HumanLlm();
    }

    @Bean
    @ConditionalOnMissingBean
    public ChatMemory chatMemory() {
        return AgentInMemoryChatMemory.builder().chatMemoryRepository(new InMemoryChatMemoryRepository()).maxMessages(1000).build();
    }

    @Bean
    @ConditionalOnMissingBean
    public ChatModelAspect chatModelAspect() {
        return new ChatModelAspect();
    }

    @Bean
    @ConditionalOnMissingBean
    public ToolManager toolManager() {
        return new ToolManager();
    }

    @Bean
    @ConditionalOnMissingBean
    public WritePlanAction writePlanAction() {
        return new WritePlanAction("writePlanAction");
    }

    @Bean
    @ConditionalOnMissingBean
    public Planner planner() {
        return new Planner();
    }

    @Bean
    @ConditionalOnMissingBean
    @Primary
    public ReactStrategy reactStrategy() {
        return new DefaultReactStrategy();
    }

    @Bean
    @ConditionalOnMissingBean
    public InOrderReactStrategy inOrderReactStrategy() {
        return new InOrderReactStrategy();
    }

    @Bean
    @ConditionalOnMissingBean
    public AutoThinkReactStrategy autoReactStrategy() {
        return new AutoThinkReactStrategy();
    }

    @Bean
    @ConditionalOnMissingBean
    ToolCallingManager toolCallingManager(ToolCallbackResolver toolCallbackResolver,
                                          ToolExecutionExceptionProcessor toolExecutionExceptionProcessor,
                                          ObjectProvider<ObservationRegistry> observationRegistry) {
        return AgentToolCallingManager.builder()
                .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
                .toolCallbackResolver(toolCallbackResolver)
                .toolExecutionExceptionProcessor(toolExecutionExceptionProcessor)
                .build();
    }

}
